#include "AutoLock.hh"
#include "QueueCore.hh"

QueueCore::QueueCore(Signal* EmptyEvent, Chain* List)
{
	State = false;
	
	DisablePush = false;
	
	SignalOfEmptyEvent = EmptyEvent;
	checkNullFV(SignalOfEmptyEvent, "SignalOfEmptyEvent is NULL.");
	
	ListOfObject = List;
	checkNullFV(ListOfObject, "ListOfObject is NULL.");
	
	State = true;
}

ResultOf QueueCore::enqueue(void* Object)
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return RETURN_ERROR;
	}
	
	if (DisablePush)
	{
		fprintf(stderr, "Cannot insert the data.\n");
		return RETURN_BLOCK;
	}
	
	ResultOf Result;
	Result = ListOfObject->addHeader(Object);
	checkErrorWR(Result, "Fail ListOfObject->addHeader.");
		
	return RETURN_OK;
}

void* QueueCore::dequeue()
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return NULL;
	}
	
	void* Object;
	Object = ListOfObject->removeTail();
	
	// NOTE
	// flush 메소드가 호출된 후로 부터 큐의 입력이 불가능하기 때문에
	// removeTail 메소드를 통해서 값은 0을 향해서 계속 줄어든다.
	// 그렇기 때문에 적어도 한번은 아래 if statement가 동작하고, DisablePush가
	// TRUE라면 항상 동작한다.
	
	if (DisablePush && ListOfObject->tellSize() == 0)
	{
		ResultOf Result;
		Result = SignalOfEmptyEvent->post();
		checkErrorWOR(Result, "Fail SignalOfEmptyEvent->post.");
	}
	
	return Object;
}

ResultOf QueueCore::fill()
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return RETURN_ERROR;
	}
	
	DisablePush = false;
	return RETURN_OK;
}

ResultOf QueueCore::flush()
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return RETURN_ERROR;
	}
	
	DisablePush = true;
	
	ResultOf Result;
	if (ListOfObject->tellSize() > 0)
	{
		Result = SignalOfEmptyEvent->pend();
		checkErrorWOR(Result, "Fail SignalOfEmptyEvent->pend.");
	}
	
	Result = SignalOfEmptyEvent->init();
	checkErrorWR(Result, "Fail SignalOfEmptyEvent->init.");
	
	return RETURN_OK;
}

USNumber QueueCore::tellSize()
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return 0;
	}
	
	return ListOfObject->tellSize();
}
